package testBusiness;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import models.DataReplace;
import models.MyConfig;
import models.MySleep;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * Created by yangxianming on 2018/12/14.
 * selenium的行为调度器
 *
 * {"result":"1/0定义的整个测试的结果"
 *  "expectList":[{"result":"1/0定义单个判断元素"},{}]
 * }
 * result:"" 说明没有定义分析数据
 */
public class ActionScheduler {
    private MyConfig myConfig;
    private ActionScheduler actionScheduler;
    private JSONObject result = new JSONObject();
    private JSONArray expectList = new JSONArray();

    public void setActionScheduler(ActionScheduler actionScheduler) {
        this.actionScheduler = actionScheduler;
    }

    //调度方法
    public JSONObject doAction(MyConfig myConfig, JSONArray data){
        expectList = new JSONArray();
        result = new JSONObject();
        result.put("result", "");    //模式是没有执行
        this.myConfig = myConfig;
        Class<?> myClass;
        Method method;
        try {
            myClass = Class.forName("testBusiness.ActionScheduler");
            //for (Object o: data)
            for (int i = 0; i < data.size(); i++){
                JSONObject oneAction = data.getJSONObject(i);

                //isJump:"1" 就跳过不执行
                if (Objects.equals(oneAction.getString("isJump"), "1"))
                    continue;

                //saveSession 这个方法还没想好则么做
                if (Objects.equals("saveSession", oneAction.getString("actionName"))){
//                    saveSessionAction();
                    continue;
                }

                //waitUntilFind
                if (Objects.equals("waitUntilFind", oneAction.getString("actionName"))){
                    if (waitUntilFindAction(myConfig.getNo1Factory().getWb(), oneAction)){
                        String t = oneAction.getJSONObject("actionDes").getString("targetText");
                        //没找到就返回错误结果
                        JSONObject except = new JSONObject();
                        except.put("result", "0");
                        except.put("errorMsg", "waitUntilFind do not find text: > " + t + " <");
                        expectList.add(except);
                        result.put("result", "0");
                        result.put("log", expectList);
                        return result;
                    }else {
                        //找到了就继续下面操作
                        continue;
                    }
                }

                //login
                if (Objects.equals("login", oneAction.getString("actionName"))){
                    if (loginAction(oneAction, myConfig)){
                        //登陆页面加载失败，用户名密码输入失败 关掉浏览器重试
                        System.out.println("login page load failed,close web driver");
                        myConfig.getNo1Factory().setWb(null);
                        //没找到就返回错误结果
                        JSONObject except = new JSONObject();
                        except.put("result", "0");
                        except.put("errorMsg", "login page load failed,close web driver");
                        expectList.add(except);
                        result.put("result", "0");
                        result.put("log", expectList);
                        return result;
                    }
                    continue;
                }

                //普通页面行为
                method = myClass.getDeclaredMethod(oneAction.getString("actionName") + "Action", WebDriver.class, JSONObject.class);
                method.setAccessible(true);
                method.invoke(actionScheduler, myConfig.getNo1Factory().getWb(), oneAction);

                //下一个也是sendKeys 和catchData 不必sleep,最后一个也不必sleep
                if (data.size()-1 == i)
                    break;
                if (Objects.equals(data.getString(i), "sendKeys") && Objects.equals(data.getString(i+1), "sendKeys"))
                    continue;
                if (Objects.equals(data.getString(i), "catchData") && Objects.equals(data.getString(i+1), "catchData"))
                    continue;
                MySleep.sleep(800);
            }
            //循环log列表有失败 外层result就是设置0
            for (Object o: expectList){
                JSONObject one = (JSONObject)o;
                if (Objects.equals(one.getString("result"), "0")){
                    result.put("result", "0");
                    break;
                }
                result.put("result", "1");
            }

        }catch (Exception e){
            e.printStackTrace();
            JSONObject except = new JSONObject();
            except.put("result", "0");
            except.put("errorMsg", e.getCause().getLocalizedMessage());
            expectList.add(except);
            result.put("result", "0");
            result.put("log", expectList);
            return result;
        }finally {
//            myConfig.getNo1Factory().getWb().close();
//            myConfig.getNo1Factory().getWb().quit();
        }

        return result;
    }

    //以下全是浏览器行为
    //特殊的保存页面驱动的行为，以便给后续脚本免登陆操作
    private void saveSessionAction(WebDriver wb, JSONObject oneAction, MyConfig myConfig){
        myConfig.setWebDriverHashMap(oneAction.getString("sessionMark"), wb);
    }

    private boolean loginAction(JSONObject oneAction, MyConfig myConfig) throws Exception {
        System.out.println("login--"+"("+oneAction.getString("textDescription")+")");
        String loginUrl = oneAction.getJSONObject("actionDes").getString("loginUrl");
        String userName = oneAction.getJSONObject("actionDes").getString("userName");
        String userNameElementType = oneAction.getJSONObject("actionDes").getString("userNameElementType");
        String userNameElementDes = oneAction.getJSONObject("actionDes").getString("userNameElementDes");
        String passWord = oneAction.getJSONObject("actionDes").getString("passWord");
        String passWordElementType = oneAction.getJSONObject("actionDes").getString("passWordElementType");
        String passWordElementDes = oneAction.getJSONObject("actionDes").getString("passWordElementDes");
        String loginMethod = oneAction.getJSONObject("actionDes").getString("loginMethod");
        String elementType = oneAction.getJSONObject("actionDes").getString("elementType");
        String elementDes = oneAction.getJSONObject("actionDes").getString("elementDes");
        String loginSuccessElementType = oneAction.getJSONObject("actionDes").getString("loginSuccessElementType");
        String loginSuccessElementDes = oneAction.getJSONObject("actionDes").getString("loginSuccessElementDes");

        //组装webdriver存储的key值 loginUrl+userName
        boolean driverIsHealth = false;
        boolean IsLoginOut = false;
        String driverKey = loginUrl + userName;
        if (!Objects.equals(myConfig.getWebDriver(driverKey), null)){
            //存在就拿出来,验证一下是否可用
            WebDriver wb = myConfig.getWebDriver(driverKey);
            try {
                wb.navigate().refresh();
                myConfig.getNo1Factory().setWb(wb);
                driverIsHealth = true;
            }catch (Exception e){
                driverIsHealth = false;
            }
        }

        if (!driverIsHealth){
            //如果driver不正常或这不存在就创建
            System.setProperty("webdriver.chrome.driver", myConfig.getDriverPath());
            WebDriver wb = new ChromeDriver();
            wb.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
            wb.manage().window().maximize();
            wb.get(loginUrl);
            try {
                getElement(wb, userNameElementType, userNameElementDes).sendKeys(userName);
                getElement(wb, passWordElementType, passWordElementDes).sendKeys(passWord);
            }catch (Exception e){
                //说明登陆页面的加载、用户名密码输入有问题。返回后尝试重新打开浏览器
                wb.close();
                wb.quit();
                return true;
            }

            //自动输入并点击登陆
            boolean needManual = false;
            if (Objects.equals("automatic", loginMethod)){

                try {
                    Thread.sleep(1000);
                    getElement(wb, elementType, elementDes).click();
                    if (Objects.equals(loginSuccessElementType, "id")){
                        wb.findElement(By.id(loginSuccessElementDes));
                    }
                    if (Objects.equals(loginSuccessElementType, "xpath")){
                        wb.findElement(By.xpath(loginSuccessElementDes));
                    }
                    //成功登陆，加入驱动列表，设置工厂驱动
                    myConfig.setWebDriverHashMap(driverKey, wb);
                    myConfig.getNo1Factory().setWb(wb);
                }catch (Exception e){
                    //如果自动登陆失败，那就手动操作一下
                    needManual = true;
                }
            }

            //如果是手动，就等待用户
            if (Objects.equals("manual", loginMethod) || needManual){
                int c = 10;
                while (c-- > 0){
                    System.out.println("---- need manual login, wait... ------");
                    try {
                        if (Objects.equals(loginSuccessElementType, "id")){
                            wb.findElement(By.id(loginSuccessElementDes));
                            break;
                        }
                        if (Objects.equals(loginSuccessElementType, "xpath")){
                            wb.findElement(By.xpath(loginSuccessElementDes));
                            break;
                        }
                    }catch (Exception e){
                        System.out.println("do not find element");
                    }
                }
                if (c > 0){
                    //成功登陆，加入驱动列表，设置工厂驱动
                    myConfig.setWebDriverHashMap(driverKey, wb);
                    myConfig.getNo1Factory().setWb(wb);
                }
            }
        }
        return false;
    }

    private boolean waitUntilFindAction(WebDriver wb, JSONObject oneAction){
        //定位元素找到目标文字后继续执行，否则终止脚本
        //无法定位元素，重复10次寻找，10次没找到终止脚本
        //返回true：没有找到目标文字。false：找到了目标文字
        System.out.println("waitUntilFind--"+"("+oneAction.getString("textDescription")+")");
        String elementType = oneAction.getJSONObject("actionDes").getString("elementType");
        String elementDes = oneAction.getJSONObject("actionDes").getString("elementDes");
        String targetText = oneAction.getJSONObject("actionDes").getString("targetText");

        int c = 10;
        while (c-- > 0){
            System.out.println("---- wait ------");
            try {
                String t = "";
                if (Objects.equals(elementType, "id")) {
                    WebElement we = wb.findElement(By.id(elementDes));
                    t = we.getText().trim();
                }
                if (Objects.equals(elementType, "xpath")){
                    WebElement we = wb.findElement(By.xpath(elementDes));
                    t = we.getText().trim();
                }
                return !Objects.equals(t, targetText);
            }catch (Exception e){
                System.out.println("do not find element");
            }
        }
        //10次也没找到目标文字说明找不到了
        return true;
    }

    private void browserAction(WebDriver wb, JSONObject oneAction){
        System.out.println("browser--"+"("+oneAction.getString("textDescription")+")");
        long waitTime = 20;
        String browserName = oneAction.getJSONObject("actionDes").getString("borwser");

        if (browserName.equals("chrome")){
            System.setProperty("webdriver.chrome.driver", myConfig.getDriverPath());
            wb = new ChromeDriver();
        }
        if (browserName.equals("ie")) {
            System.setProperty("webdriver.ie.driver", myConfig.getDriverPath());
            wb = new InternetExplorerDriver();
        }
        if (browserName.equals("firefox")){
            System.setProperty("webdriver.firefox.driver", myConfig.getDriverPath());
            wb = new FirefoxDriver();
        }
        wb.manage().timeouts().implicitlyWait(waitTime, TimeUnit.SECONDS);
        myConfig.getNo1Factory().setWb(wb);
    }

    private void pageAction(WebDriver wb, JSONObject oneAction){
        System.out.println("page--"+"("+oneAction.getString("textDescription")+")");
        String page = oneAction.getJSONObject("actionDes").getString("page");
        wb.get(page);

        myConfig.getNo1Factory().setWb(wb);
    }

    private void listClickAction(WebDriver wb, JSONObject oneAction) throws Exception{
        System.out.println("listClick--"+"("+oneAction.getString("textDescription")+")");
        String elementType = oneAction.getJSONObject("actionDes").getString("elementType");
        String elementDes = oneAction.getJSONObject("actionDes").getString("elementDes");
        String tarText = oneAction.getJSONObject("actionDes").getString("tarText");

        List<WebElement> list = getElements(wb, elementType, elementDes);
        for (WebElement webElement: list){
//            System.out.println(webElement.getText());
            if (Objects.equals(webElement.getText().trim(), tarText)){
                webElement.click();
                break;
            }
        }

        myConfig.getNo1Factory().setWb(wb);
    }

    private void sendKeysAction(WebDriver wb, JSONObject oneAction) throws Exception{
        System.out.println("sendKeys--"+"("+oneAction.getString("textDescription")+")");
        String elementType = oneAction.getJSONObject("actionDes").getString("elementType");
        String elementDes = oneAction.getJSONObject("actionDes").getString("elementDes");
        String content = oneAction.getJSONObject("actionDes").getString("content");
        content = DataReplace.replace(myConfig.getReflect(), content);

        WebElement we = getElement(wb, elementType, elementDes);
        we.sendKeys(Keys.chord(Keys.CONTROL, "a"));
        we.sendKeys(Keys.DELETE);
        Thread.sleep(10);
        we.sendKeys(content);

        myConfig.getNo1Factory().setWb(wb);
    }

    private void clickAction(WebDriver wb, JSONObject oneAction) throws Exception{
        System.out.println("click--"+"("+oneAction.getString("textDescription")+")");
        String elementType = oneAction.getJSONObject("actionDes").getString("elementType");
        String elementDes = oneAction.getJSONObject("actionDes").getString("elementDes");

        getElement(wb, elementType, elementDes).click();
        myConfig.getNo1Factory().setWb(wb);
    }

    private void catchDataAction(WebDriver wb, JSONObject oneAction) throws Exception{
        System.out.println("catchData--"+"("+oneAction.getString("textDescription")+")");
        String elementType = oneAction.getJSONObject("actionDes").getString("elementType");
        String elementDes = oneAction.getJSONObject("actionDes").getString("elementDes");
        String howToGet = oneAction.getJSONObject("actionDes").getString("howToGet");
        String attributeName = oneAction.getJSONObject("actionDes").getString("attributeName");
        String reflectName = oneAction.getJSONObject("actionDes").getString("reflectName");
        String data = "";

        WebElement targetElement = getElement(wb, elementType, elementDes);
        //这里添加howToGet不同类型
        if (Objects.equals(howToGet, "getText")){
            data = targetElement.getText();
        }
        if (Objects.equals(howToGet, "getSize")){
            data = String.valueOf(getElements(wb, elementType, elementDes).size());
        }
        if (Objects.equals(howToGet, "getAttribute")){
            data = targetElement.getAttribute(attributeName);
        }

        myConfig.setReflectHash(reflectName, data);

        //如果expect的type=""那就返回空，也就是说没有预期校验
        JSONObject expect = oneAction.getJSONObject("actionDes").getJSONObject("expect");
        expect.put("left", DataReplace.replace(myConfig.getReflect(), expect.getString("left")));
        expect.put("right", DataReplace.replace(myConfig.getReflect(), expect.getString("right")));
        if (Objects.equals(expect.getString("type"), "")){
            expect.put("result", "");
            expectList.add(expect);
            return;
        }
        AnalysisScheduler analysisScheduler = new AnalysisScheduler();
        String b = analysisScheduler.parse(myConfig, expect);
        expect.put("result", b);
        expectList.add(expect);
        result.put("log", expectList);
    }
    private void switchFrameAction(WebDriver wb, JSONObject oneAction) throws Exception{
        System.out.println("switchFrame--"+"("+oneAction.getString("textDescription")+")");
        String switchTo = oneAction.getJSONObject("actionDes").getString("switchTo");
        String elementType = oneAction.getJSONObject("actionDes").getString("elementType");
        String elementDes = oneAction.getJSONObject("actionDes").getString("elementDes");

        if (Objects.equals(switchTo, "son")){
            wb = wb.switchTo().frame(getElement(wb, elementType, elementDes));
        }
        if (Objects.equals(switchTo, "default")){
            wb = wb.switchTo().defaultContent();
        }

        myConfig.getNo1Factory().setWb(wb);
    }
    private void javaScriptAction(WebDriver wb, JSONObject oneAction) throws Exception{
        String js = oneAction.getJSONObject("actionDes").getString("js");
        System.out.println("javaScript-- " + js +" ("+oneAction.getString("textDescription")+")");
        JavascriptExecutor javascriptExecutor = (JavascriptExecutor)wb;
        javascriptExecutor.executeScript(js);

        myConfig.getNo1Factory().setWb(wb);
    }

    private void waitTimeAction(WebDriver wb, JSONObject oneAction) throws Exception{
        String waitTime = oneAction.getJSONObject("actionDes").getString("waitTime");
        System.out.println("waitTime-- " + waitTime + "ms");
        try {
            long sleepTime = Long.valueOf(waitTime);
            Thread.sleep(sleepTime);
        }catch (Exception e){
            System.out.println("waitTime error, use default, sleep 1000ms" );
            try {
                Thread.sleep(1000);
            }catch (Exception e1){
                e1.printStackTrace();
            }
        }

        myConfig.getNo1Factory().setWb(wb);
    }

    private WebElement getElement(WebDriver wb, String elementType, String elementDes){
        //通过elementType获取页面元素位置
        if (Objects.equals(elementType, "id"))
            return wb.findElement(By.id(elementDes));
        if (Objects.equals(elementType, "xpath"))
            return wb.findElement(By.xpath(elementDes));
        return null;
    }
    private List<WebElement> getElements(WebDriver wb, String elementType, String elementDes){
        //通过elementType获取页面元素位置
        if (Objects.equals(elementType, "id"))
            return wb.findElements(By.id(elementDes));
        if (Objects.equals(elementType, "xpath"))
            return wb.findElements(By.xpath(elementDes));
        return null;
    }
}
